/* * Copyright (C) 2012 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.android.tools.lint.checks; import com.android.tools.lint.detector.api.Detector; import com.android.tools.lint.detector.api.Project; @SuppressWarnings("javadoc") public class ApiDetectorTest extends AbstractCheckTest { @Override protected Detector getDetector() { return new ApiDetector(); } public void testXmlApi1() throws Exception { assertEquals( "res/color/colors.xml:9: Error: @android:color/holo_red_light requires API level 14 (current min is 1) [NewApi]\n" + " <item name=\"android:windowBackground\"> @android:color/holo_red_light </item>\n" + " ^\n" + "res/layout/layout.xml:9: Error: View requires API level 5 (current min is 1): <QuickContactBadge> [NewApi]\n" + " <QuickContactBadge\n" + " ^\n" + "res/layout/layout.xml:15: Error: View requires API level 11 (current min is 1): <CalendarView> [NewApi]\n" + " <CalendarView\n" + " ^\n" + "res/layout/layout.xml:21: Error: View requires API level 14 (current min is 1): <GridLayout> [NewApi]\n" + " <GridLayout\n" + " ^\n" + "res/layout/layout.xml:22: Error: @android:attr/actionBarSplitStyle requires API level 14 (current min is 1) [NewApi]\n" + " foo=\"@android:attr/actionBarSplitStyle\"\n" + " ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n" + "res/layout/layout.xml:23: Error: @android:color/holo_red_light requires API level 14 (current min is 1) [NewApi]\n" + " bar=\"@android:color/holo_red_light\"\n" + " ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n" + "res/values/themes.xml:9: Error: @android:color/holo_red_light requires API level 14 (current min is 1) [NewApi]\n" + " <item name=\"android:windowBackground\"> @android:color/holo_red_light </item>\n" + " ^\n" + "7 errors, 0 warnings\n" + "", lintProject( "apicheck/minsdk1.xml=>AndroidManifest.xml", "apicheck/layout.xml=>res/layout/layout.xml", "apicheck/themes.xml=>res/values/themes.xml", "apicheck/themes.xml=>res/color/colors.xml" )); } public void testXmlApi14() throws Exception { assertEquals( "No warnings.", lintProject( "apicheck/minsdk14.xml=>AndroidManifest.xml", "apicheck/layout.xml=>res/layout/layout.xml", "apicheck/themes.xml=>res/values/themes.xml", "apicheck/themes.xml=>res/color/colors.xml" )); } public void testXmlApiFolderVersion11() throws Exception { assertEquals( "res/color-v11/colors.xml:9: Error: @android:color/holo_red_light requires API level 14 (current min is 1) [NewApi]\n" + " <item name=\"android:windowBackground\"> @android:color/holo_red_light </item>\n" + " ^\n" + "res/layout-v11/layout.xml:21: Error: View requires API level 14 (current min is 1): <GridLayout> [NewApi]\n" + " <GridLayout\n" + " ^\n" + "res/layout-v11/layout.xml:22: Error: @android:attr/actionBarSplitStyle requires API level 14 (current min is 1) [NewApi]\n" + " foo=\"@android:attr/actionBarSplitStyle\"\n" + " ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n" + "res/layout-v11/layout.xml:23: Error: @android:color/holo_red_light requires API level 14 (current min is 1) [NewApi]\n" + " bar=\"@android:color/holo_red_light\"\n" + " ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n" + "res/values-v11/themes.xml:9: Error: @android:color/holo_red_light requires API level 14 (current min is 1) [NewApi]\n" + " <item name=\"android:windowBackground\"> @android:color/holo_red_light </item>\n" + " ^\n" + "5 errors, 0 warnings\n" + "", lintProject( "apicheck/minsdk1.xml=>AndroidManifest.xml", "apicheck/layout.xml=>res/layout-v11/layout.xml", "apicheck/themes.xml=>res/values-v11/themes.xml", "apicheck/themes.xml=>res/color-v11/colors.xml" )); } public void testXmlApiFolderVersion14() throws Exception { assertEquals( "No warnings.", lintProject( "apicheck/minsdk1.xml=>AndroidManifest.xml", "apicheck/layout.xml=>res/layout-v14/layout.xml", "apicheck/themes.xml=>res/values-v14/themes.xml", "apicheck/themes.xml=>res/color-v14/colors.xml" )); } public void testApi1() throws Exception { assertEquals( "src/foo/bar/ApiCallTest.java:18: Error: Class requires API level 8 (current min is 1): org.w3c.dom.DOMLocator [NewApi]\n" + " public void method(Chronometer chronometer, DOMLocator locator) {\n" + " ~~~~~~~~~~\n" + "src/foo/bar/ApiCallTest.java:20: Error: Call requires API level 11 (current min is 1): android.app.Activity#getActionBar [NewApi]\n" + " getActionBar(); // API 11\n" + " ~~~~~~~~~~~~\n" + "src/foo/bar/ApiCallTest.java:23: Error: Class requires API level 8 (current min is 1): org.w3c.dom.DOMError [NewApi]\n" + " DOMError error = null; // API 8\n" + " ~~~~~~~~\n" + "src/foo/bar/ApiCallTest.java:24: Error: Class requires API level 8 (current min is 1): org.w3c.dom.DOMErrorHandler [NewApi]\n" + " Class<?> clz = DOMErrorHandler.class; // API 8\n" + " ~~~~~~~~~~~~~~~\n" + "src/foo/bar/ApiCallTest.java:27: Error: Call requires API level 3 (current min is 1): android.widget.Chronometer#getOnChronometerTickListener [NewApi]\n" + " chronometer.getOnChronometerTickListener(); // API 3 \n" + " ~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n" + "src/foo/bar/ApiCallTest.java:30: Error: Call requires API level 11 (current min is 1): android.widget.Chronometer#setTextIsSelectable [NewApi]\n" + " chronometer.setTextIsSelectable(true); // API 11\n" + " ~~~~~~~~~~~~~~~~~~~\n" + "src/foo/bar/ApiCallTest.java:33: Error: Field requires API level 11 (current min is 1): dalvik.bytecode.OpcodeInfo#MAXIMUM_VALUE [NewApi]\n" + " int field = OpcodeInfo.MAXIMUM_VALUE; // API 11\n" + " ~~~~~~~~~~~~~\n" + "src/foo/bar/ApiCallTest.java:38: Error: Class requires API level 14 (current min is 1): android.app.ApplicationErrorReport.BatteryInfo [NewApi]\n" + " BatteryInfo batteryInfo = getReport().batteryInfo;\n" + " ~~~~~~~~~~~\n" + "src/foo/bar/ApiCallTest.java:38: Error: Field requires API level 14 (current min is 1): android.app.ApplicationErrorReport#batteryInfo [NewApi]\n" + " BatteryInfo batteryInfo = getReport().batteryInfo;\n" + " ~~~~~~~~~~~\n" + "src/foo/bar/ApiCallTest.java:41: Error: Field requires API level 11 (current min is 1): android.graphics.PorterDuff.Mode#OVERLAY [NewApi]\n" + " Mode mode = PorterDuff.Mode.OVERLAY; // API 11\n" + " ~~~~~~~\n" + "src/foo/bar/ApiCallTest.java:45: Error: Class requires API level 14 (current min is 1): android.widget.GridLayout [NewApi]\n" + " GridLayout getGridLayout() { // API 14\n" + " ~~~~~~~~~~~~~\n" + "src/foo/bar/ApiCallTest.java:49: Error: Class requires API level 14 (current min is 1): android.app.ApplicationErrorReport [NewApi]\n" + " private ApplicationErrorReport getReport() {\n" + " ~~~~~~~~~\n" + "12 errors, 0 warnings\n", lintProject( "apicheck/classpath=>.classpath", "apicheck/minsdk1.xml=>AndroidManifest.xml", "apicheck/ApiCallTest.java.txt=>src/foo/bar/ApiCallTest.java", "apicheck/ApiCallTest.class.data=>bin/classes/foo/bar/ApiCallTest.class" )); } public void testApi2() throws Exception { assertEquals( "src/foo/bar/ApiCallTest.java:18: Error: Class requires API level 8 (current min is 2): org.w3c.dom.DOMLocator [NewApi]\n" + " public void method(Chronometer chronometer, DOMLocator locator) {\n" + " ~~~~~~~~~~\n" + "src/foo/bar/ApiCallTest.java:20: Error: Call requires API level 11 (current min is 2): android.app.Activity#getActionBar [NewApi]\n" + " getActionBar(); // API 11\n" + " ~~~~~~~~~~~~\n" + "src/foo/bar/ApiCallTest.java:23: Error: Class requires API level 8 (current min is 2): org.w3c.dom.DOMError [NewApi]\n" + " DOMError error = null; // API 8\n" + " ~~~~~~~~\n" + "src/foo/bar/ApiCallTest.java:24: Error: Class requires API level 8 (current min is 2): org.w3c.dom.DOMErrorHandler [NewApi]\n" + " Class<?> clz = DOMErrorHandler.class; // API 8\n" + " ~~~~~~~~~~~~~~~\n" + "src/foo/bar/ApiCallTest.java:27: Error: Call requires API level 3 (current min is 2): android.widget.Chronometer#getOnChronometerTickListener [NewApi]\n" + " chronometer.getOnChronometerTickListener(); // API 3 \n" + " ~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n" + "src/foo/bar/ApiCallTest.java:30: Error: Call requires API level 11 (current min is 2): android.widget.Chronometer#setTextIsSelectable [NewApi]\n" + " chronometer.setTextIsSelectable(true); // API 11\n" + " ~~~~~~~~~~~~~~~~~~~\n" + "src/foo/bar/ApiCallTest.java:33: Error: Field requires API level 11 (current min is 2): dalvik.bytecode.OpcodeInfo#MAXIMUM_VALUE [NewApi]\n" + " int field = OpcodeInfo.MAXIMUM_VALUE; // API 11\n" + " ~~~~~~~~~~~~~\n" + "src/foo/bar/ApiCallTest.java:38: Error: Class requires API level 14 (current min is 2): android.app.ApplicationErrorReport.BatteryInfo [NewApi]\n" + " BatteryInfo batteryInfo = getReport().batteryInfo;\n" + " ~~~~~~~~~~~\n" + "src/foo/bar/ApiCallTest.java:38: Error: Field requires API level 14 (current min is 2): android.app.ApplicationErrorReport#batteryInfo [NewApi]\n" + " BatteryInfo batteryInfo = getReport().batteryInfo;\n" + " ~~~~~~~~~~~\n" + "src/foo/bar/ApiCallTest.java:41: Error: Field requires API level 11 (current min is 2): android.graphics.PorterDuff.Mode#OVERLAY [NewApi]\n" + " Mode mode = PorterDuff.Mode.OVERLAY; // API 11\n" + " ~~~~~~~\n" + "src/foo/bar/ApiCallTest.java:45: Error: Class requires API level 14 (current min is 2): android.widget.GridLayout [NewApi]\n" + " GridLayout getGridLayout() { // API 14\n" + " ~~~~~~~~~~~~~\n" + "src/foo/bar/ApiCallTest.java:49: Error: Class requires API level 14 (current min is 2): android.app.ApplicationErrorReport [NewApi]\n" + " private ApplicationErrorReport getReport() {\n" + " ~~~~~~~~~\n" + "12 errors, 0 warnings\n", lintProject( "apicheck/classpath=>.classpath", "apicheck/minsdk2.xml=>AndroidManifest.xml", "apicheck/ApiCallTest.java.txt=>src/foo/bar/ApiCallTest.java", "apicheck/ApiCallTest.class.data=>bin/classes/foo/bar/ApiCallTest.class" )); } public void testApi4() throws Exception { assertEquals( "src/foo/bar/ApiCallTest.java:18: Error: Class requires API level 8 (current min is 4): org.w3c.dom.DOMLocator [NewApi]\n" + " public void method(Chronometer chronometer, DOMLocator locator) {\n" + " ~~~~~~~~~~\n" + "src/foo/bar/ApiCallTest.java:20: Error: Call requires API level 11 (current min is 4): android.app.Activity#getActionBar [NewApi]\n" + " getActionBar(); // API 11\n" + " ~~~~~~~~~~~~\n" + "src/foo/bar/ApiCallTest.java:23: Error: Class requires API level 8 (current min is 4): org.w3c.dom.DOMError [NewApi]\n" + " DOMError error = null; // API 8\n" + " ~~~~~~~~\n" + "src/foo/bar/ApiCallTest.java:24: Error: Class requires API level 8 (current min is 4): org.w3c.dom.DOMErrorHandler [NewApi]\n" + " Class<?> clz = DOMErrorHandler.class; // API 8\n" + " ~~~~~~~~~~~~~~~\n" + "src/foo/bar/ApiCallTest.java:30: Error: Call requires API level 11 (current min is 4): android.widget.Chronometer#setTextIsSelectable [NewApi]\n" + " chronometer.setTextIsSelectable(true); // API 11\n" + " ~~~~~~~~~~~~~~~~~~~\n" + "src/foo/bar/ApiCallTest.java:33: Error: Field requires API level 11 (current min is 4): dalvik.bytecode.OpcodeInfo#MAXIMUM_VALUE [NewApi]\n" + " int field = OpcodeInfo.MAXIMUM_VALUE; // API 11\n" + " ~~~~~~~~~~~~~\n" + "src/foo/bar/ApiCallTest.java:38: Error: Class requires API level 14 (current min is 4): android.app.ApplicationErrorReport.BatteryInfo [NewApi]\n" + " BatteryInfo batteryInfo = getReport().batteryInfo;\n" + " ~~~~~~~~~~~\n" + "src/foo/bar/ApiCallTest.java:38: Error: Field requires API level 14 (current min is 4): android.app.ApplicationErrorReport#batteryInfo [NewApi]\n" + " BatteryInfo batteryInfo = getReport().batteryInfo;\n" + " ~~~~~~~~~~~\n" + "src/foo/bar/ApiCallTest.java:41: Error: Field requires API level 11 (current min is 4): android.graphics.PorterDuff.Mode#OVERLAY [NewApi]\n" + " Mode mode = PorterDuff.Mode.OVERLAY; // API 11\n" + " ~~~~~~~\n" + "src/foo/bar/ApiCallTest.java:45: Error: Class requires API level 14 (current min is 4): android.widget.GridLayout [NewApi]\n" + " GridLayout getGridLayout() { // API 14\n" + " ~~~~~~~~~~~~~\n" + "src/foo/bar/ApiCallTest.java:49: Error: Class requires API level 14 (current min is 4): android.app.ApplicationErrorReport [NewApi]\n" + " private ApplicationErrorReport getReport() {\n" + " ~~~~~~~~~\n" + "11 errors, 0 warnings\n", lintProject( "apicheck/classpath=>.classpath", "apicheck/minsdk4.xml=>AndroidManifest.xml", "apicheck/ApiCallTest.java.txt=>src/foo/bar/ApiCallTest.java", "apicheck/ApiCallTest.class.data=>bin/classes/foo/bar/ApiCallTest.class" )); } public void testApi10() throws Exception { assertEquals( "src/foo/bar/ApiCallTest.java:20: Error: Call requires API level 11 (current min is 10): android.app.Activity#getActionBar [NewApi]\n" + " getActionBar(); // API 11\n" + " ~~~~~~~~~~~~\n" + "src/foo/bar/ApiCallTest.java:30: Error: Call requires API level 11 (current min is 10): android.widget.Chronometer#setTextIsSelectable [NewApi]\n" + " chronometer.setTextIsSelectable(true); // API 11\n" + " ~~~~~~~~~~~~~~~~~~~\n" + "src/foo/bar/ApiCallTest.java:33: Error: Field requires API level 11 (current min is 10): dalvik.bytecode.OpcodeInfo#MAXIMUM_VALUE [NewApi]\n" + " int field = OpcodeInfo.MAXIMUM_VALUE; // API 11\n" + " ~~~~~~~~~~~~~\n" + "src/foo/bar/ApiCallTest.java:38: Error: Class requires API level 14 (current min is 10): android.app.ApplicationErrorReport.BatteryInfo [NewApi]\n" + " BatteryInfo batteryInfo = getReport().batteryInfo;\n" + " ~~~~~~~~~~~\n" + "src/foo/bar/ApiCallTest.java:38: Error: Field requires API level 14 (current min is 10): android.app.ApplicationErrorReport#batteryInfo [NewApi]\n" + " BatteryInfo batteryInfo = getReport().batteryInfo;\n" + " ~~~~~~~~~~~\n" + "src/foo/bar/ApiCallTest.java:41: Error: Field requires API level 11 (current min is 10): android.graphics.PorterDuff.Mode#OVERLAY [NewApi]\n" + " Mode mode = PorterDuff.Mode.OVERLAY; // API 11\n" + " ~~~~~~~\n" + "src/foo/bar/ApiCallTest.java:45: Error: Class requires API level 14 (current min is 10): android.widget.GridLayout [NewApi]\n" + " GridLayout getGridLayout() { // API 14\n" + " ~~~~~~~~~~~~~\n" + "src/foo/bar/ApiCallTest.java:49: Error: Class requires API level 14 (current min is 10): android.app.ApplicationErrorReport [NewApi]\n" + " private ApplicationErrorReport getReport() {\n" + " ~~~~~~~~~\n" + "8 errors, 0 warnings\n", lintProject( "apicheck/classpath=>.classpath", "apicheck/minsdk10.xml=>AndroidManifest.xml", "apicheck/ApiCallTest.java.txt=>src/foo/bar/ApiCallTest.java", "apicheck/ApiCallTest.class.data=>bin/classes/foo/bar/ApiCallTest.class" )); } public void testApi14() throws Exception { assertEquals( "No warnings.", lintProject( "apicheck/classpath=>.classpath", "apicheck/minsdk14.xml=>AndroidManifest.xml", "apicheck/ApiCallTest.java.txt=>src/foo/bar/ApiCallTest.java", "apicheck/ApiCallTest.class.data=>bin/classes/foo/bar/ApiCallTest.class" )); } public void testInheritStatic() throws Exception { assertEquals( "src/foo/bar/ApiCallTest5.java:16: Error: Call requires API level 11 (current min is 2): android.view.View#resolveSizeAndState [NewApi]\n" + " int measuredWidth = View.resolveSizeAndState(widthMeasureSpec,\n" + " ~~~~~~~~~~~~~~~~~~~\n" + "src/foo/bar/ApiCallTest5.java:18: Error: Call requires API level 11 (current min is 2): android.view.View#resolveSizeAndState [NewApi]\n" + " int measuredHeight = resolveSizeAndState(heightMeasureSpec,\n" + " ~~~~~~~~~~~~~~~~~~~\n" + "src/foo/bar/ApiCallTest5.java:20: Error: Call requires API level 11 (current min is 2): android.view.View#combineMeasuredStates [NewApi]\n" + " View.combineMeasuredStates(0, 0);\n" + " ~~~~~~~~~~~~~~~~~~~~~\n" + "src/foo/bar/ApiCallTest5.java:21: Error: Call requires API level 11 (current min is 2): android.view.View#combineMeasuredStates [NewApi]\n" + " ApiCallTest5.combineMeasuredStates(0, 0);\n" + " ~~~~~~~~~~~~~~~~~~~~~\n" + "4 errors, 0 warnings\n" + "", lintProject( "apicheck/classpath=>.classpath", "apicheck/minsdk2.xml=>AndroidManifest.xml", "apicheck/ApiCallTest5.java.txt=>src/foo/bar/ApiCallTest5.java", "apicheck/ApiCallTest5.class.data=>bin/classes/foo/bar/ApiCallTest5.class" )); } public void testInheritLocal() throws Exception { // Test virtual dispatch in a local class which extends some other local class (which // in turn extends an Android API) assertEquals( "src/test/pkg/ApiCallTest3.java:10: Error: Call requires API level 11 (current min is 1): android.app.Activity#getActionBar [NewApi]\n" + " getActionBar(); // API 11\n" + " ~~~~~~~~~~~~\n" + "1 errors, 0 warnings\n" + "", lintProject( "apicheck/classpath=>.classpath", "apicheck/minsdk1.xml=>AndroidManifest.xml", "apicheck/Intermediate.java.txt=>src/test/pkg/Intermediate.java", "apicheck/ApiCallTest3.java.txt=>src/test/pkg/ApiCallTest3.java", "apicheck/ApiCallTest3.class.data=>bin/classes/test/pkg/ApiCallTest3.class", "apicheck/Intermediate.class.data=>bin/classes/test/pkg/Intermediate.class" )); } public void testViewClassLayoutReference() throws Exception { assertEquals( "res/layout/view.xml:9: Error: View requires API level 5 (current min is 1): <QuickContactBadge> [NewApi]\n" + " <view\n" + " ^\n" + "res/layout/view.xml:16: Error: View requires API level 11 (current min is 1): <CalendarView> [NewApi]\n" + " <view\n" + " ^\n" + "res/layout/view.xml:24: Error: ?android:attr/dividerHorizontal requires API level 11 (current min is 1) [NewApi]\n" + " unknown=\"?android:attr/dividerHorizontal\"\n" + " ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n" + "res/layout/view.xml:25: Error: ?android:attr/textColorLinkInverse requires API level 11 (current min is 1) [NewApi]\n" + " android:textColor=\"?android:attr/textColorLinkInverse\" />\n" + " ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n" + "4 errors, 0 warnings\n" + "", lintProject( "apicheck/minsdk1.xml=>AndroidManifest.xml", "apicheck/view.xml=>res/layout/view.xml" )); } public void testIOException() throws Exception { // See http://code.google.com/p/android/issues/detail?id=35190 assertEquals( "src/test/pkg/ApiCallTest6.java:8: Error: Call requires API level 9 (current min is 1): new java.io.IOException [NewApi]\n" + " IOException ioException = new IOException(throwable);\n" + " ~~~~~~~~~~~\n" + "1 errors, 0 warnings\n", lintProject( "apicheck/classpath=>.classpath", "apicheck/minsdk1.xml=>AndroidManifest.xml", "apicheck/Intermediate.java.txt=>src/test/pkg/Intermediate.java", "apicheck/ApiCallTest6.java.txt=>src/test/pkg/ApiCallTest6.java", "apicheck/ApiCallTest6.class.data=>bin/classes/test/pkg/ApiCallTest6.class" )); } // Test suppressing errors -- on classes, methods etc. public void testSuppress() throws Exception { assertEquals( // These errors are correctly -not- suppressed because they // appear in method3 (line 74-98) which is annotated with a // @SuppressLint annotation specifying only an unrelated issue id "src/foo/bar/SuppressTest1.java:74: Error: Class requires API level 8 (current min is 1): org.w3c.dom.DOMLocator [NewApi]\n" + " public void method3(Chronometer chronometer, DOMLocator locator) {\n" + " ~~~~~~~~~~\n" + "src/foo/bar/SuppressTest1.java:76: Error: Call requires API level 11 (current min is 1): android.app.Activity#getActionBar [NewApi]\n" + " getActionBar(); // API 11\n" + " ~~~~~~~~~~~~\n" + "src/foo/bar/SuppressTest1.java:79: Error: Class requires API level 8 (current min is 1): org.w3c.dom.DOMError [NewApi]\n" + " DOMError error = null; // API 8\n" + " ~~~~~~~~\n" + "src/foo/bar/SuppressTest1.java:80: Error: Class requires API level 8 (current min is 1): org.w3c.dom.DOMErrorHandler [NewApi]\n" + " Class<?> clz = DOMErrorHandler.class; // API 8\n" + " ~~~~~~~~~~~~~~~\n" + "src/foo/bar/SuppressTest1.java:83: Error: Call requires API level 3 (current min is 1): android.widget.Chronometer#getOnChronometerTickListener [NewApi]\n" + " chronometer.getOnChronometerTickListener(); // API 3\n" + " ~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n" + "src/foo/bar/SuppressTest1.java:86: Error: Call requires API level 11 (current min is 1): android.widget.Chronometer#setTextIsSelectable [NewApi]\n" + " chronometer.setTextIsSelectable(true); // API 11\n" + " ~~~~~~~~~~~~~~~~~~~\n" + "src/foo/bar/SuppressTest1.java:89: Error: Field requires API level 11 (current min is 1): dalvik.bytecode.OpcodeInfo#MAXIMUM_VALUE [NewApi]\n" + " int field = OpcodeInfo.MAXIMUM_VALUE; // API 11\n" + " ~~~~~~~~~~~~~\n" + "src/foo/bar/SuppressTest1.java:94: Error: Class requires API level 14 (current min is 1): android.app.ApplicationErrorReport.BatteryInfo [NewApi]\n" + " BatteryInfo batteryInfo = getReport().batteryInfo;\n" + " ~~~~~~~~~~~\n" + "src/foo/bar/SuppressTest1.java:94: Error: Field requires API level 14 (current min is 1): android.app.ApplicationErrorReport#batteryInfo [NewApi]\n" + " BatteryInfo batteryInfo = getReport().batteryInfo;\n" + " ~~~~~~~~~~~\n" + "src/foo/bar/SuppressTest1.java:97: Error: Field requires API level 11 (current min is 1): android.graphics.PorterDuff.Mode#OVERLAY [NewApi]\n" + " Mode mode = PorterDuff.Mode.OVERLAY; // API 11\n" + " ~~~~~~~\n" + // Note: These annotations are within the methods, not ON the methods, so they have // no effect (because they don't end up in the bytecode) "src/foo/bar/SuppressTest4.java:16: Error: Class requires API level 14 (current min is 1): android.app.ApplicationErrorReport [NewApi]\n" + " ApplicationErrorReport report = null;\n" + " ~~~~~~~~~~~~~~~~~~~~~~\n" + "src/foo/bar/SuppressTest4.java:19: Error: Class requires API level 14 (current min is 1): android.app.ApplicationErrorReport.BatteryInfo [NewApi]\n" + " BatteryInfo batteryInfo = report.batteryInfo;\n" + " ~~~~~~~~~~~\n" + "src/foo/bar/SuppressTest4.java:19: Error: Field requires API level 14 (current min is 1): android.app.ApplicationErrorReport#batteryInfo [NewApi]\n" + " BatteryInfo batteryInfo = report.batteryInfo;\n" + " ~~~~~~~~~~~\n" + "13 errors, 0 warnings\n", lintProject( "apicheck/classpath=>.classpath", "apicheck/minsdk1.xml=>AndroidManifest.xml", "apicheck/SuppressTest1.java.txt=>src/foo/bar/SuppressTest1.java", "apicheck/SuppressTest1.class.data=>bin/classes/foo/bar/SuppressTest1.class", "apicheck/SuppressTest2.java.txt=>src/foo/bar/SuppressTest2.java", "apicheck/SuppressTest2.class.data=>bin/classes/foo/bar/SuppressTest2.class", "apicheck/SuppressTest3.java.txt=>src/foo/bar/SuppressTest3.java", "apicheck/SuppressTest3.class.data=>bin/classes/foo/bar/SuppressTest3.class", "apicheck/SuppressTest4.java.txt=>src/foo/bar/SuppressTest4.java", "apicheck/SuppressTest4.class.data=>bin/classes/foo/bar/SuppressTest4.class" )); } public void testSuppressInnerClasses() throws Exception { assertEquals( // These errors are correctly -not- suppressed because they // appear outside the middle inner class suppressing its own errors // and its child's errors "src/test/pkg/ApiCallTest4.java:9: Error: Call requires API level 14 (current min is 1): new android.widget.GridLayout [NewApi]\n" + " new GridLayout(null, null, 0);\n" + " ~~~~~~~~~~\n" + "src/test/pkg/ApiCallTest4.java:38: Error: Call requires API level 14 (current min is 1): new android.widget.GridLayout [NewApi]\n" + " new GridLayout(null, null, 0);\n" + " ~~~~~~~~~~\n" + "2 errors, 0 warnings\n", lintProject( "apicheck/classpath=>.classpath", "apicheck/minsdk1.xml=>AndroidManifest.xml", "apicheck/ApiCallTest4.java.txt=>src/test/pkg/ApiCallTest4.java", "apicheck/ApiCallTest4.class.data=>bin/classes/test/pkg/ApiCallTest4.class", "apicheck/ApiCallTest4$1.class.data=>bin/classes/test/pkg/ApiCallTest4$1.class", "apicheck/ApiCallTest4$InnerClass1.class.data=>bin/classes/test/pkg/ApiCallTest4$InnerClass1.class", "apicheck/ApiCallTest4$InnerClass2.class.data=>bin/classes/test/pkg/ApiCallTest4$InnerClass2.class", "apicheck/ApiCallTest4$InnerClass1$InnerInnerClass1.class.data=>bin/classes/test/pkg/ApiCallTest4$InnerClass1$InnerInnerClass1.class" )); } public void testApiTargetAnnotation() throws Exception { assertEquals( "src/foo/bar/ApiTargetTest.java:13: Error: Class requires API level 8 (current min is 1): org.w3c.dom.DOMErrorHandler [NewApi]\n" + " Class<?> clz = DOMErrorHandler.class; // API 8\n" + " ~~~~~~~~~~~~~~~\n" + "src/foo/bar/ApiTargetTest.java:25: Error: Class requires API level 8 (current min is 4): org.w3c.dom.DOMErrorHandler [NewApi]\n" + " Class<?> clz = DOMErrorHandler.class; // API 8\n" + " ~~~~~~~~~~~~~~~\n" + "src/foo/bar/ApiTargetTest.java:39: Error: Class requires API level 8 (current min is 7): org.w3c.dom.DOMErrorHandler [NewApi]\n" + " Class<?> clz = DOMErrorHandler.class; // API 8\n" + " ~~~~~~~~~~~~~~~\n" + "3 errors, 0 warnings\n" + "", lintProject( "apicheck/classpath=>.classpath", "apicheck/minsdk1.xml=>AndroidManifest.xml", "apicheck/ApiTargetTest.java.txt=>src/foo/bar/ApiTargetTest.java", "apicheck/ApiTargetTest.class.data=>bin/classes/foo/bar/ApiTargetTest.class", "apicheck/ApiTargetTest$LocalClass.class.data=>bin/classes/foo/bar/ApiTargetTest$LocalClass.class" )); } public void testTargetAnnotationInner() throws Exception { assertEquals( "src/test/pkg/ApiTargetTest2.java:32: Error: Call requires API level 14 (current min is 3): new android.widget.GridLayout [NewApi]\n" + " new GridLayout(null, null, 0);\n" + " ~~~~~~~~~~\n" + "1 errors, 0 warnings\n", lintProject( "apicheck/classpath=>.classpath", "apicheck/minsdk1.xml=>AndroidManifest.xml", "apicheck/ApiTargetTest2.java.txt=>src/test/pkg/ApiTargetTest2.java", "apicheck/ApiTargetTest2.class.data=>bin/classes/test/pkg/ApiTargetTest2.class", "apicheck/ApiTargetTest2$1.class.data=>bin/classes/test/pkg/ApiTargetTest2$1.class", "apicheck/ApiTargetTest2$1$2.class.data=>bin/classes/test/pkg/ApiTargetTest2$1$2.class", "apicheck/ApiTargetTest2$1$1.class.data=>bin/classes/test/pkg/ApiTargetTest2$1$1.class" )); } public void testSkipAndroidSupportInAospHalf() throws Exception { String expected; if (System.getenv("ANDROID_BUILD_TOP") != null) { expected = "No warnings."; } else { expected = "bin/classes/android/support/foo/Foo.class: Error: Class requires API level 8 (current min is 1): org.w3c.dom.DOMError [NewApi]\n" + "1 errors, 0 warnings\n"; } assertEquals( expected, lintProject( "apicheck/classpath=>.classpath", "apicheck/minsdk1.xml=>AndroidManifest.xml", "apicheck/ApiCallTest2.java.txt=>src/src/android/support/foo/Foo.java", "apicheck/ApiCallTest2.class.data=>bin/classes/android/support/foo/Foo.class" )); } public void testSuper() throws Exception { // See http://code.google.com/p/android/issues/detail?id=36384 assertEquals( "src/test/pkg/ApiCallTest7.java:8: Error: Call requires API level 9 (current min is 4): new java.io.IOException [NewApi]\n" + " super(message, cause); // API 9\n" + " ~~~~~\n" + "src/test/pkg/ApiCallTest7.java:12: Error: Call requires API level 9 (current min is 4): new java.io.IOException [NewApi]\n" + " super.toString(); throw new IOException((Throwable) null); // API 9\n" + " ~~~~~~~~~~~\n" + "2 errors, 0 warnings\n", lintProject( "apicheck/classpath=>.classpath", "apicheck/minsdk4.xml=>AndroidManifest.xml", "apicheck/ApiCallTest7.java.txt=>src/test/pkg/ApiCallTest7.java", "apicheck/ApiCallTest7.class.data=>bin/classes/test/pkg/ApiCallTest7.class" )); } public void testEnums() throws Exception { // See http://code.google.com/p/android/issues/detail?id=36951 assertEquals( "src/test/pkg/TestEnum.java:26: Error: Enum value requires API level 11 (current min is 4): android.graphics.PorterDuff.Mode#OVERLAY [NewApi]\n" + " case OVERLAY: {\n" + " ~~~~~~~\n" + "src/test/pkg/TestEnum.java:37: Error: Enum value requires API level 11 (current min is 4): android.graphics.PorterDuff.Mode#OVERLAY [NewApi]\n" + " case OVERLAY: {\n" + " ~~~~~~~\n" + "src/test/pkg/TestEnum.java:58: Error: Class requires API level 11 (current min is 4): android.renderscript.Element.DataType [NewApi]\n" + " public static void test4(final android.renderscript.Element.DataType type) {\n" + " ~~~~~~~~\n" + "src/test/pkg/TestEnum.java:61: Error: Enum for switch requires API level 11 (current min is 4): android.renderscript.Element.DataType [NewApi]\n" + " switch (type) {\n" + " ^\n" + "4 errors, 0 warnings\n", lintProject( "apicheck/classpath=>.classpath", "apicheck/minsdk4.xml=>AndroidManifest.xml", "apicheck/TestEnum.java.txt=>src/test/pkg/TestEnum.java", "apicheck/TestEnum.class.data=>bin/classes/test/pkg/TestEnum.class" )); } @Override public String getSuperClass(Project project, String name) { // For testInterfaceInheritance if (name.equals("android/database/sqlite/SQLiteStatement")) { return "android/database/sqlite/SQLiteProgram"; } else if (name.equals("android/database/sqlite/SQLiteProgram")) { return "android/database/sqlite/SQLiteClosable"; } else if (name.equals("android/database/sqlite/SQLiteClosable")) { return "java/lang/Object"; } return null; } public void testInterfaceInheritance() throws Exception { // See http://code.google.com/p/android/issues/detail?id=38004 assertEquals( "No warnings.", lintProject( "apicheck/classpath=>.classpath", "apicheck/minsdk4.xml=>AndroidManifest.xml", "apicheck/CloseTest.java.txt=>src/test/pkg/CloseTest.java", "apicheck/CloseTest.class.data=>bin/classes/test/pkg/CloseTest.class" )); } public void testInnerClassPositions() throws Exception { // See http://code.google.com/p/android/issues/detail?id=38113 assertEquals( "src/test/pkg/ApiCallTest8.java:8: Error: Class requires API level 8 (current min is 4): android.text.style.LeadingMarginSpan.LeadingMarginSpan2 [NewApi]\n" + " LeadingMarginSpan.LeadingMarginSpan2 span = null; \n" + " ~~~~~~~~~~~~~~~~~~\n" + "1 errors, 0 warnings\n", lintProject( "apicheck/classpath=>.classpath", "apicheck/minsdk4.xml=>AndroidManifest.xml", "apicheck/ApiCallTest8.java.txt=>src/test/pkg/ApiCallTest8.java", "apicheck/ApiCallTest8.class.data=>bin/classes/test/pkg/ApiCallTest8.class" )); } public void testManifestReferences() throws Exception { assertEquals( "AndroidManifest.xml:15: Error: @android:style/Theme.Holo requires API level 11 (current min is 4) [NewApi]\n" + " android:theme=\"@android:style/Theme.Holo\" >\n" + " ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n" + "1 errors, 0 warnings\n", lintProject( "apicheck/classpath=>.classpath", "apicheck/holomanifest.xml=>AndroidManifest.xml" )); } public void testSuppressFieldAnnotations() throws Exception { // See http://code.google.com/p/android/issues/detail?id=38626 assertEquals( "src/test/pkg/ApiCallTest9.java:9: Error: Call requires API level 14 (current min is 4): new android.widget.GridLayout [NewApi]\n" + " private GridLayout field1 = new GridLayout(null);\n" + " ~~~~~~~~~~\n" + "src/test/pkg/ApiCallTest9.java:12: Error: Call requires API level 14 (current min is 4): new android.widget.GridLayout [NewApi]\n" + " private static GridLayout field2 = new GridLayout(null);\n" + " ~~~~~~~~~~\n" + "2 errors, 0 warnings\n", lintProject( "apicheck/classpath=>.classpath", "apicheck/minsdk4.xml=>AndroidManifest.xml", "apicheck/ApiCallTest9.java.txt=>src/test/pkg/ApiCallTest9.java", "apicheck/ApiCallTest9.class.data=>bin/classes/test/pkg/ApiCallTest9.class" )); } public void test38195() throws Exception { // See http://code.google.com/p/android/issues/detail?id=38195 assertEquals( "bin/classes/TestLint.class: Error: Call requires API level 16 (current min is 4): new android.database.SQLException [NewApi]\n" + "bin/classes/TestLint.class: Error: Call requires API level 9 (current min is 4): java.lang.String#isEmpty [NewApi]\n" + "bin/classes/TestLint.class: Error: Call requires API level 9 (current min is 4): new java.sql.SQLException [NewApi]\n" + "3 errors, 0 warnings\n", lintProject( "apicheck/classpath=>.classpath", "apicheck/minsdk4.xml=>AndroidManifest.xml", /* Compiled from "TestLint.java" public class test.pkg.TestLint extends java.lang.Object{ public test.pkg.TestLint(); Code: 0: aload_0 1: invokespecial #8; //Method java/lang/Object."<init>":()V 4: return public void test(java.lang.Exception) throws java.lang.Exception; Code: 0: ldc #19; //String 2: invokevirtual #21; //Method java/lang/String.isEmpty:()Z 5: istore_2 6: new #27; //class java/sql/SQLException 9: dup 10: ldc #29; //String error on upgrade: 12: aload_1 13: invokespecial #31; //Method java/sql/SQLException."<init>": (Ljava/lang/String;Ljava/lang/Throwable;)V 16: athrow public void test2(java.lang.Exception) throws java.lang.Exception; Code: 0: new #39; //class android/database/SQLException 3: dup 4: ldc #29; //String error on upgrade: 6: aload_1 7: invokespecial #41; //Method android/database/SQLException. "<init>":(Ljava/lang/String;Ljava/lang/Throwable;)V 10: athrow } */ "apicheck/TestLint.class.data=>bin/classes/TestLint.class" )); } public void testAllowLocalMethodsImplementingInaccessible() throws Exception { // See http://code.google.com/p/android/issues/detail?id=39030 assertEquals( "src/test/pkg/ApiCallTest10.java:25: Error: Call requires API level 14 (current min is 4): android.view.View#onPopulateAccessibilityEvent [NewApi]\n" + " super.onPopulateAccessibilityEvent(event); // Valid lint warning\n" + " ~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n" + "src/test/pkg/ApiCallTest10.java:31: Error: Call requires API level 14 (current min is 4): android.view.View#dispatchGenericFocusedEvent [NewApi]\n" + " return super.dispatchGenericFocusedEvent(event); // Should flag this\n" + " ~~~~~~~~~~~~~~~~~~~~~~~~~~~\n" + "src/test/pkg/ApiCallTest10.java:40: Error: Call requires API level 14 (current min is 4): android.view.View#dispatchHoverEvent [NewApi]\n" + " dispatchHoverEvent(null);\n" + " ~~~~~~~~~~~~~~~~~~\n" + "3 errors, 0 warnings\n", lintProject( "apicheck/classpath=>.classpath", "apicheck/minsdk4.xml=>AndroidManifest.xml", "apicheck/ApiCallTest10.java.txt=>src/test/pkg/ApiCallTest10.java", "apicheck/ApiCallTest10.class.data=>bin/classes/test/pkg/ApiCallTest10.class" )); } }